package com.wwd.common.lock;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;

@Component
public class RedisLock extends AbstractDistributedLock{
	
	private final Logger log=LoggerFactory.getLogger(AbstractDistributedLock.class);
	
	private static ThreadLocal<String> currentValue = new ThreadLocal<String>();
	
	public static final String LUA_DEL_SCRIPT;
	
	@Autowired
	private RedisTemplate<Object,Object> redisTemplate;
	
	static {
		StringBuilder sb=new StringBuilder();
		sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
		sb.append("then ");
		sb.append("    return redis.call(\"del\",KEYS[1]) ");
		sb.append("else ");
		sb.append("    return 0 ");
		sb.append("end ");
		LUA_DEL_SCRIPT=sb.toString();
	}

	@Override
	public boolean lock(String key, long expire, int retry) {
		boolean success = setCommand(key,expire);
		while((!success) && retry > 0) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				log.error("get lock error");
			}
			success = setCommand(key,expire);
			log.info("try get lock " + key + " in " + retry + " times");
			retry--;
		}
		if(success) {
			return true;
		}
		return false;
	}

	@Override
	public boolean unlock(String key) {
		List<String> keys = Arrays.asList(key);
		List<String> values = Arrays.asList(currentValue.get());
		Long result = redisTemplate.execute((RedisCallback<Long>) connection ->{
			Object commands = connection.getNativeConnection();
			if(commands instanceof JedisCluster) {
				return (Long)((JedisCluster)commands).eval(LUA_DEL_SCRIPT, keys, values);
			}
			if(commands instanceof Jedis) {
				return (Long)((Jedis)commands).eval(LUA_DEL_SCRIPT,keys,values);
			}
			return 0L;
		});
		return result != null && result > 0;
	}

	private boolean setCommand(String key,long expire) {
		String uuid = UUID.randomUUID().toString();
		String result = redisTemplate.execute((RedisCallback<String>) connection ->{
			JedisCommands commands = (JedisCommands)connection.getNativeConnection();
			currentValue.set(uuid);
			return commands.set(key, uuid, "NX", "EX", expire);
		});
		return !StringUtils.isEmpty(result);
	}
}
